package com.qlogic.commons.web.filter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.qlogic.commons.utils.strings.StringUtils;

import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPOutputStream;

public class GzipFilter extends Filter {

    /**
     * Performs initialisation.
     */
    protected void doInit() throws Exception {
    }

    /**
     * A template method that performs any Filter specific destruction tasks.
     * Called from {@link #destroy()}
     */
    protected void doDestroy() {
        //noop
    }

    /**
     * Performs the filtering for a request.
     */
    protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
                            final FilterChain chain) throws Exception {
    	
    	boolean processRequest = 
    		exclude == null || !StringUtils.existsIn (request.getContentType (), exclude);
    	
        if (!isIncluded(request, response) && processRequest && acceptsEncoding(request, "gzip")) {
            // Client accepts zipped content
            console(request.getRequestURL() + ". Writing with gzip compression");

            // Create a gzip stream
            final ByteArrayOutputStream compressed = new ByteArrayOutputStream();
            final GZIPOutputStream gzout = new GZIPOutputStream(compressed);

            // Handle the request
            final GenericResponseWrapper wrapper = new GenericResponseWrapper(response, gzout);
            chain.doFilter(request, wrapper);
            wrapper.flush();

            gzout.close();

            //Saneness checks
            byte[] compressedBytes = compressed.toByteArray();
            boolean shouldGzippedBodyBeZero = ResponseUtil.shouldGzippedBodyBeZero(compressedBytes, request);
            boolean shouldBodyBeZero = ResponseUtil.shouldBodyBeZero(request, wrapper.getStatus());
            if (shouldGzippedBodyBeZero || shouldBodyBeZero) {
                compressedBytes = new byte[0];
            }

            // Write the zipped body
            ResponseUtil.addGzipHeader(response);
            response.setContentLength(compressedBytes.length);


            response.getOutputStream().write(compressedBytes);
        } else {
            // Client does not accept zipped content - don't bother zipping
            console (request.getRequestURL()
                        + ". Writing without gzip compression because the request does not accept gzip.");
            chain.doFilter(request, response);
        }
    }



    /**
     * Checks if the request uri is an include.
     * These cannot be gzipped.
     */
    private boolean isIncluded(final HttpServletRequest request, final HttpServletResponse response) {
        final String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
        final boolean includeRequest = uri != null;

        if (includeRequest && getLogType() == DEBUG) {
            console (request.getRequestURL() + " resulted in an include request. This is unusable, because" +
                    " the response will be assembled into the overrall response. Not gzipping.");
        }
        return includeRequest;
    }

    /**
     * Determine whether the user agent accepts GZIP encoding. This feature is part of HTTP1.1.
     * If a browser accepts GZIP encoding it will advertise this by including in its HTTP header:
     * <p/>
     * <code>
     * Accept-Encoding: gzip
     * </code>
     * <p/>
     * Requests which do not accept GZIP encoding fall into the following categories:
     * <ul>
     * <li>Old browsers, notably IE 5 on Macintosh.
     * <li>Internet Explorer through a proxy. By default HTTP1.1 is enabled but disabled when going
     * through a proxy. 90% of non gzip requests seen on the Internet are caused by this.
     * </ul>
     * As of September 2004, about 34% of Internet requests do not accept GZIP encoding.
     *
     * @param request
     * @return true, if the User Agent request accepts GZIP encoding
     */
    protected boolean acceptsGzipEncoding(HttpServletRequest request) {
        return acceptsEncoding(request, "gzip");
    }

	public String getLoggerName() {
		return "GZIP FILTER";
	}

	public void setLoggerName(String loggerName) {
	}


}
